-
Notifications
You must be signed in to change notification settings - Fork 536
[TOOL-4531] Dashboard: Add Token Asset creation wizard #7081
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TOOL-4531] Dashboard: Add Token Asset creation wizard #7081
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #7081 +/- ##
=======================================
Coverage 55.61% 55.61%
=======================================
Files 902 902
Lines 58177 58177
Branches 4085 4085
=======================================
Hits 32356 32356
Misses 25716 25716
Partials 105 105
🚀 New features to boost your workflow:
|
size-limit report 📦
|
7f8c64d
to
c37589e
Compare
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
Outdated
Show resolved
Hide resolved
c37589e
to
dbe4965
Compare
...ard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx (1)
367-390
:⚠️ Potential issuePrevent memory leaks by revoking object URLs
The
URL.createObjectURL
call in the download button creates a unique object URL that should be revoked when no longer needed to prevent memory leaks.function DownloadExampleCSVButton() { return ( <Button size="sm" onClick={() => { const link = document.createElement("a"); const exampleData = [ { address: "0x000000000000000000000000000000000000dEaD", quantity: "2", }, { address: "thirdweb.eth", quantity: "1", }, ]; const csv = `address,quantity\n${exampleData .map((o) => `${o.address},${o.quantity}`) .join("\n")}`; const blob = new Blob([csv], { type: "text/csv" }); link.href = URL.createObjectURL(blob); link.download = "airdrop.csv"; link.click(); + // Clean up the URL object to prevent memory leaks + setTimeout(() => { + URL.revokeObjectURL(link.href); + }, 100); }} > <ArrowDownToLineIcon className="mr-2 size-4" /> Download Example CSV </Button> ); }
♻️ Duplicate comments (9)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx (1)
62-69
: Add error boundary to complement SuspenseThe Suspense component is used without a complementary error boundary, which could lead to unhandled promise rejections.
Consider wrapping the Suspense content with an ErrorBoundary component to gracefully handle errors:
import { ErrorBoundary } from '@/components/error-boundary'; // Use your preferred error boundary <ErrorBoundary fallback={<div>Something went wrong. Please try again.</div>}> <Suspense fallback={<GenericLoadingPage />}> <AssetsPageAsync teamId={team.id} projectId={project.id} authToken={authToken} client={client} /> </Suspense> </ErrorBoundary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (2)
83-85
: 🛠️ Refactor suggestionAdd form validation before proceeding to next step
Currently, the code transitions to the next step without validating that the current form is valid. This could lead to incomplete or invalid data being used later in the flow.
onNext={() => { + // Validate form before proceeding + tokenInfoForm.trigger().then((isValid) => { + if (isValid) { setStep("distribution"); + } + }); }}
97-99
: 🛠️ Refactor suggestionAdd form validation before proceeding to launch step
Similar to the previous step transition, there's no validation before moving to the launch step.
onNext={() => { + // Validate form before proceeding + tokenDistributionForm.trigger().then((isValid) => { + if (isValid) { setStep("launch"); + } + }); }}apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx (3)
53-56
:⚠️ Potential issueGuard against non-numeric quantities to prevent
NaN
in totals
Number(curr.quantity)
will produceNaN
for invalid or empty strings, causing the whole sum to becomeNaN
.- const totalAirdropSupply = airdropAddresses.reduce( - (acc, curr) => acc + Number(curr.quantity), - 0, - ); + const totalAirdropSupply = airdropAddresses.reduce((acc, curr) => { + const qty = Number(curr.quantity); + return acc + (Number.isFinite(qty) ? qty : 0); + }, 0);
430-430
: 🛠️ Refactor suggestionAdd validation for quantity values
The table correctly displays validation for invalid addresses but doesn't indicate or validate if quantities are non-numeric, which could lead to
NaN
values in calculations.- <TableCell>{item.quantity}</TableCell> + <TableCell> + {!isNaN(Number(item.quantity)) ? ( + item.quantity + ) : ( + <div className="flex flex-row items-center gap-2"> + <CircleAlertIcon className="size-4 text-red-500" /> + <span className="font-bold text-red-500"> + {item.quantity} + </span> + </div> + )} + </TableCell>
193-200
: 🛠️ Refactor suggestionValidate quantity values during CSV parsing
The CSV parser doesn't validate if quantities are valid numbers, which could lead to issues later in calculations.
// CSV parser for airdrop data const csvParser = (items: AirdropAddressInput[]): AirdropAddressInput[] => { return items .map(({ address, quantity }) => { + const trimmedQuantity = (quantity || "1").trim(); + // Check if quantity is a valid number + const isValidQuantity = !isNaN(Number(trimmedQuantity)) && Number(trimmedQuantity) > 0; return { address: (address || "").trim(), - quantity: (quantity || "1").trim(), + quantity: isValidQuantity ? trimmedQuantity : "1", + hasInvalidQuantity: !isValidQuantity && quantity !== undefined, }; }) .filter(({ address }) => address !== ""); };apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (3)
48-57
: Add error handling for file uploads.The FileInput component handles file selection but doesn't appear to have error handling for common upload issues like file size limits or format validation failures beyond the
accept
prop.Consider adding explicit error handling:
<FileInput accept={{ "image/*": [] }} value={form.watch("image")} setValue={(file) => form.setValue("image", file, { shouldTouch: true, }) } + maxSizeInBytes={5 * 1024 * 1024} // Example: 5MB limit + onError={(error) => { + form.setError("image", { + type: "manual", + message: error.message + }); + }} className="rounded-lg border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background" />
100-110
: Handle chain data load errors and add selection validationCurrently,
useAllChainsData
swallows any fetch errors andSingleNetworkSelector
simply stays disabled (showing "Loading Chains…") ifallChains
is empty—on a failed network request it never recovers. Also, there's no form‐level feedback if the user never picks a chain.Suggested fixes:
• In
apps/dashboard/src/hooks/chains/allChains.ts
– ReturnisLoading
,isError
, anderror
from the underlyinguseQuery
(allChainsQuery
) in addition to populating the store.
• Inapps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
(around lines 127–134)
– Accept/loading and error flags and pass them down toSelectWithSearch
.
– Render:
– a loading spinner or "Loading chains…" whileisLoading
– an inline error message (e.g. "Failed to load networks. Please retry") with a retry handler whenisError
• Inapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
(lines 100–110)
– Before renderingSingleNetworkSelector
, check for and display any loading/error state exposed by the hook.
– Add form validation forchain
(e.g.required
) and display a validation error if the user submits without selecting a chain.
176-191
: Add URL validation for social URLs.The social URL field should validate that the input is a properly formatted URL.
Consider using a pattern or custom validation in your form schema for the URL field:
<FormField control={form.control} name={`socialUrls.${index}.url`} render={({ field }) => ( <FormItem className="flex-1"> <FormControl> <Input {...field} placeholder="https://..." aria-label="Platform URL" + type="url" /> </FormControl> <FormMessage /> </FormItem> )} />
Also ensure your form schema (in form.ts) includes URL validation for these fields.
🧹 Nitpick comments (6)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (6)
6-6
: Remove empty import statement.There's an unnecessary empty import from the card component that should be removed.
-import {} from "@/components/ui/card";
50-55
: Optimize form performance with controlled inputs.Using
form.watch()
for the file input value can cause unnecessary re-renders. Consider refactoring to use a controlled component pattern to improve performance.<FileInput accept={{ "image/*": [] }} - value={form.watch("image")} + value={form.getValues("image")} setValue={(file) => form.setValue("image", file, { shouldTouch: true, }) } className="rounded-lg border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background" />
104-107
: Optimize chain selection to avoid unnecessary re-renders.Similar to the file input, using
form.watch("chain")
can cause unnecessary re-renders. Consider using a more efficient approach:<SingleNetworkSelector className="bg-background" client={props.client} - chainId={Number(form.watch("chain"))} + chainId={Number(form.getValues("chain"))} onChange={(chain) => { form.setValue("chain", chain.toString()); + form.trigger("chain"); // Trigger validation }} disableChainId />
149-149
: Improve accessibility with proper heading hierarchy.The social URLs section uses an h2 heading, but it's not clear if this follows the proper heading hierarchy of the page. Ensure heading levels are used properly for accessibility.
- <h2 className="mb-2 font-medium text-sm">Social URLs</h2> + <h3 className="mb-2 font-medium text-sm">Social URLs</h3>
32-40
: Add form error boundary.Consider adding an error boundary around your form to gracefully handle any unexpected rendering errors.
<Form {...form}> <form onSubmit={form.handleSubmit(props.onNext)}> + <ErrorBoundary fallback={<div>Something went wrong with the form. Please try again or contact support.</div>}> <StepCard page="info" title="Token Information" prevButton={undefined} nextButton={{ type: "submit", }} > + </ErrorBoundary>
212-212
: Add default values when adding new social URLs.When adding a new social URL, consider providing default platform values like "Twitter" or "Discord" to make it easier for users:
- onClick={() => append({ platform: "", url: "" })} + onClick={() => { + // If no platforms exist yet, suggest common ones + const existingPlatforms = form.getValues("socialUrls").map(item => item.platform.toLowerCase()); + let defaultPlatform = ""; + + if (!existingPlatforms.includes("twitter") && !existingPlatforms.includes("x")) { + defaultPlatform = "Twitter"; + } else if (!existingPlatforms.includes("discord")) { + defaultPlatform = "Discord"; + } else if (!existingPlatforms.includes("website")) { + defaultPlatform = "Website"; + } + + append({ platform: defaultPlatform, url: "" }); + }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (36)
apps/dashboard/src/@/actions/revalidate.ts
(1 hunks)apps/dashboard/src/@/components/blocks/distribution-chart.tsx
(1 hunks)apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
(1 hunks)apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
(1 hunks)apps/dashboard/src/components/buttons/MismatchButton.tsx
(2 hunks)apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
(1 hunks)apps/dashboard/src/components/contract-components/import-contract/modal.tsx
(7 hunks)apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
(6 hunks)apps/dashboard/src/components/contract-components/tables/contract-table.tsx
(6 hunks)apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
(3 hunks)apps/dashboard/src/global.css
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (31)
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
- apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
- apps/dashboard/src/global.css
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
- apps/dashboard/src/@/actions/revalidate.ts
- apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
- apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
- apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
- apps/dashboard/src/components/buttons/MismatchButton.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
- apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
- apps/dashboard/src/components/contract-components/tables/contract-table.tsx
- apps/dashboard/src/@/components/blocks/distribution-chart.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
- apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
- apps/dashboard/src/components/contract-components/import-contract/modal.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
<Dialog | ||
open={isModalOpen} | ||
// do not set onOpenChange | ||
> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Prevent accidental dialog dismissal
The comment on line 212 indicates that onOpenChange
should not be set, but omitting it entirely allows the dialog to be closed by clicking outside or pressing escape.
<Dialog
open={isModalOpen}
- // do not set onOpenChange
+ onOpenChange={(open) => {
+ // Only allow closing through the Close button
+ if (open === false) {
+ // Don't allow closing from outside clicks or close icon
+ return;
+ }
+ setIsModalOpen(open);
+ }}
>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<Dialog | |
open={isModalOpen} | |
// do not set onOpenChange | |
> | |
<Dialog | |
open={isModalOpen} | |
onOpenChange={(open) => { | |
// Only allow closing through the Close button | |
if (open === false) { | |
// Don't allow closing from outside clicks or close icon | |
return; | |
} | |
setIsModalOpen(open); | |
}} | |
> |
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
around lines 210 to 213, the Dialog component lacks an onOpenChange handler,
which causes the dialog to be dismissible by clicking outside or pressing
escape. To prevent accidental dismissal, explicitly set onOpenChange to a no-op
function or a handler that prevents closing, ensuring the dialog remains open
unless explicitly closed by your intended controls.
updateStatus(index, "error"); | ||
launchTracking("error"); | ||
console.error(error); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling by capturing and displaying error details
When errors occur during token deployment, the current implementation only logs to the console without showing detailed feedback to users.
function createSequenceExecutorFn(
index: number,
executeFn: (values: CreateAssetFormValues) => Promise<void>,
) {
return async () => {
updateStatus(index, "pending");
try {
await executeFn(formValues);
updateStatus(index, "completed");
// start next one
const nextStep = initialSteps[index + 1];
if (nextStep) {
// do not use await next step
nextStep.execute();
} else {
launchTracking("success");
props.onLaunchSuccess();
}
} catch (error) {
- updateStatus(index, "error");
+ // Extract error message for display
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
+ setSteps((prev) => {
+ return [
+ ...prev.slice(0, index),
+ { ...prev[index], status: "error", errorMessage },
+ ...prev.slice(index + 1),
+ ] as MultiStepState[];
+ });
launchTracking("error");
console.error(error);
}
};
}
You'll also need to update the MultiStepState
type to include the optional errorMessage
property and modify the MultiStepStatus
component to display this message.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
updateStatus(index, "error"); | |
launchTracking("error"); | |
console.error(error); | |
} | |
function createSequenceExecutorFn( | |
index: number, | |
executeFn: (values: CreateAssetFormValues) => Promise<void>, | |
) { | |
return async () => { | |
updateStatus(index, "pending"); | |
try { | |
await executeFn(formValues); | |
updateStatus(index, "completed"); | |
// start next one | |
const nextStep = initialSteps[index + 1]; | |
if (nextStep) { | |
// do not use await next step | |
nextStep.execute(); | |
} else { | |
launchTracking("success"); | |
props.onLaunchSuccess(); | |
} | |
} catch (error) { | |
// Extract error message for display | |
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; | |
setSteps((prev) => { | |
return [ | |
...prev.slice(0, index), | |
{ ...prev[index], status: "error", errorMessage }, | |
...prev.slice(index + 1), | |
] as MultiStepState[]; | |
}); | |
launchTracking("error"); | |
console.error(error); | |
} | |
}; | |
} |
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
around lines 89 to 92, improve error handling by capturing the error message and
updating the state with an errorMessage property. Modify the MultiStepState type
to include an optional errorMessage field, update the updateStatus call to pass
this message, and adjust the MultiStepStatus component to display the
errorMessage to users for better feedback.
values={{ | ||
...tokenInfoForm.getValues(), | ||
...tokenDistributionForm.getValues(), | ||
}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate combined form data before passing to LaunchTokenStatus
When combining form data for the launch step, no validation is performed to ensure the complete dataset is valid.
{step === "launch" && (
<LaunchTokenStatus
client={props.client}
onLaunchSuccess={props.onLaunchSuccess}
onPrevious={() => {
setStep("distribution");
}}
createTokenFunctions={props.createTokenFunctions}
+ // Ensure both forms are valid before proceeding
+ isValid={tokenInfoForm.formState.isValid && tokenDistributionForm.formState.isValid}
+ form={{
+ tokenInfoForm,
+ tokenDistributionForm,
+ }}
values={{
...tokenInfoForm.getValues(),
...tokenDistributionForm.getValues(),
}}
/>
)}
Then in LaunchTokenStatus component, add validation before beginning the launch sequence:
// In LaunchTokenStatus component
async function handleSubmitClick() {
// Re-validate both forms before proceeding
const isTokenInfoValid = await props.form.tokenInfoForm.trigger();
const isDistributionValid = await props.form.tokenDistributionForm.trigger();
if (!isTokenInfoValid || !isDistributionValid) {
console.log("Forms are not valid");
return;
}
// Continue with launch process...
}
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
around lines 111 to 114, the combined form data is passed to LaunchTokenStatus
without validation. To fix this, add validation logic inside the
LaunchTokenStatus component before starting the launch sequence by triggering
validation on both tokenInfoForm and tokenDistributionForm. If either form is
invalid, prevent the launch process from continuing and handle the invalid state
appropriately.
<Form {...form}> | ||
<form onSubmit={form.handleSubmit(props.onNext)}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve form submission handling.
The form submission directly calls the onNext
function without additional error handling or user feedback. Consider adding a loading state and error handling during form submission.
<Form {...form}>
- <form onSubmit={form.handleSubmit(props.onNext)}>
+ <form onSubmit={form.handleSubmit(
+ (data) => {
+ try {
+ props.onNext();
+ } catch (error) {
+ console.error('Error in form submission:', error);
+ // Display error to user
+ }
+ },
+ (errors) => {
+ console.log('Validation errors:', errors);
+ // Optionally scroll to first error
+ }
+ )}>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<Form {...form}> | |
<form onSubmit={form.handleSubmit(props.onNext)}> | |
<Form {...form}> | |
<form onSubmit={form.handleSubmit( | |
(data) => { | |
try { | |
props.onNext(); | |
} catch (error) { | |
console.error('Error in form submission:', error); | |
// Display error to user | |
} | |
}, | |
(errors) => { | |
console.log('Validation errors:', errors); | |
// Optionally scroll to first error | |
} | |
)}> |
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
around lines 31 to 32, the form submission calls onNext directly without
managing loading or errors. To fix this, implement a loading state that
activates when the form is submitted and deactivates after completion or error.
Add try-catch around the submission logic to handle errors gracefully and
provide user feedback, such as displaying error messages or disabling the submit
button while loading.
7e90eb0
to
24a556b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (7)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (3)
83-85
: 🛠️ Refactor suggestionAdd form validation before proceeding to next step.
Currently, the code transitions to the next step without validating that the current form is valid. This could lead to incomplete or invalid data being used later in the flow.
onNext={() => { + // Validate form before proceeding + tokenInfoForm.trigger().then((isValid) => { + if (isValid) { setStep("distribution"); + } + }); }}
97-99
: 🛠️ Refactor suggestionAdd form validation before proceeding to launch step.
Similar to the previous step transition, there's no validation before moving to the launch step.
onNext={() => { + // Validate form before proceeding + tokenDistributionForm.trigger().then((isValid) => { + if (isValid) { setStep("launch"); + } + }); }}
111-114
: 🛠️ Refactor suggestionValidate combined form data before passing to LaunchTokenStatus.
When combining form data for the launch step, no validation is performed to ensure the complete dataset is valid.
{step === "launch" && ( <LaunchTokenStatus client={props.client} onLaunchSuccess={props.onLaunchSuccess} onPrevious={() => { setStep("distribution"); }} createTokenFunctions={props.createTokenFunctions} + // Ensure both forms are valid before proceeding + isValid={tokenInfoForm.formState.isValid && tokenDistributionForm.formState.isValid} + form={{ + tokenInfoForm, + tokenDistributionForm, + }} values={{ ...tokenInfoForm.getValues(), ...tokenDistributionForm.getValues(), }} /> )}apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (4)
31-32
: Improve form submission handling.The form submission directly calls the
onNext
function without additional error handling or user feedback. Consider adding a loading state and error handling during form submission.<Form {...form}> - <form onSubmit={form.handleSubmit(props.onNext)}> + <form onSubmit={form.handleSubmit( + (data) => { + try { + props.onNext(); + } catch (error) { + console.error('Error in form submission:', error); + // Display error to user + } + }, + (errors) => { + console.log('Validation errors:', errors); + // Optionally scroll to first error + } + )}>
100-110
: Handle chain data load errors and add selection validation.Currently, there's no explicit handling for potential network errors or failed chain data loading in the SingleNetworkSelector component.
The SingleNetworkSelector component should handle error states from chain data fetching, and the form should validate chain selection. Consider:
- Updating useAllChainsData to return isLoading, isError, and error flags
- Modifying SingleNetworkSelector to accept these flags and render appropriate loading/error states
- Adding form validation to require chain selection
#!/bin/bash # Check current implementation of SingleNetworkSelector error handling rg -n "export function SingleNetworkSelector" -A10 -B10 apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
48-57
: 🛠️ Refactor suggestionAdd error handling for file uploads.
The FileInput component handles file selection but doesn't appear to have error handling for common upload issues like file size limits or format validation failures beyond the
accept
prop.<FileInput accept={{ "image/*": [] }} value={form.watch("image")} setValue={(file) => form.setValue("image", file, { shouldTouch: true, }) } + maxSizeInBytes={5 * 1024 * 1024} // Example: 5MB limit + onError={(error) => { + form.setError("image", { + type: "manual", + message: error.message + }); + }} className="rounded-lg border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background" />
176-191
: 🛠️ Refactor suggestionAdd URL validation for social URLs.
The social URL field should validate that the input is a properly formatted URL.
<FormField control={form.control} name={`socialUrls.${index}.url`} render={({ field }) => ( <FormItem className="flex-1"> <FormControl> <Input {...field} placeholder="https://..." aria-label="Platform URL" + type="url" /> </FormControl> <FormMessage /> </FormItem> )} />
Also ensure your form schema (in form.ts) includes URL validation for these fields.
🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (1)
3-6
: Remove unused empty imports.There are several empty imports that should be removed as they add unnecessary code.
-import {} from "@/components/blocks/multi-step-status/multi-step-status"; -import {} from "@/components/ui/dialog"; import { zodResolver } from "@hookform/resolvers/zod"; -import {} from "lucide-react"; import { useState } from "react";apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (1)
6-6
: Remove unused empty import.The card component is imported but not used. Remove this empty import.
-import {} from "@/components/ui/card";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (36)
apps/dashboard/src/@/actions/revalidate.ts
(1 hunks)apps/dashboard/src/@/components/blocks/distribution-chart.tsx
(1 hunks)apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
(1 hunks)apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
(1 hunks)apps/dashboard/src/components/buttons/MismatchButton.tsx
(2 hunks)apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
(1 hunks)apps/dashboard/src/components/contract-components/import-contract/modal.tsx
(7 hunks)apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
(6 hunks)apps/dashboard/src/components/contract-components/tables/contract-table.tsx
(6 hunks)apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
(3 hunks)apps/dashboard/src/global.css
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
🚧 Files skipped from review as they are similar to previous changes (33)
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
- apps/dashboard/src/global.css
- apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
- apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
- apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
- apps/dashboard/src/@/actions/revalidate.ts
- apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
- apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
- apps/dashboard/src/components/buttons/MismatchButton.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
- apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
- apps/dashboard/src/@/components/blocks/distribution-chart.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
- apps/dashboard/src/components/contract-components/tables/contract-table.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
- apps/dashboard/src/components/contract-components/import-contract/modal.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Lint Packages
- GitHub Check: Size
- GitHub Check: Analyze (javascript)
Merge activity
|
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR introduces several enhancements and features related to asset management, including deployment tracking, UI updates, and new forms for creating and managing tokens. It also refines existing components and adds new functionalities for improved user experience. ### Detailed summary - Added `deploymentType` and `contractType` properties in various components. - Introduced `revalidatePathAction` for path revalidation. - Updated UI components for better asset management. - Enhanced forms for token creation with validation. - Implemented multi-step status tracking for token launch processes. - Improved error handling and user feedback in forms. - Added new charts for token distribution visualization. > The following files were skipped due to too many changes: `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx`, `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a comprehensive asset management interface for creating, importing, and managing ERC-20 tokens within team and project dashboards. - Added a multi-step token creation flow with form validation, including token info, distribution, sale settings, airdrop CSV upload, and launch process with real-time status tracking. - Implemented visual token allocation charts and multi-step progress indicators for enhanced user experience. - Added a dedicated "Assets" section in the sidebar with a "New" badge. - Added a horizontal segmented distribution bar chart component for visualizing token allocations. - Introduced a multi-step status component to track asynchronous step executions with retry capabilities. - Added token airdrop CSV upload and validation feature with user-friendly UI and error handling. - Added token sale configuration section with price and allocation inputs. - Enhanced import modal to support both contract and asset imports with contextual UI and behavior. - Added new pages and components to support token asset creation and management workflows, including cards for asset actions. - Added server-side path revalidation support for pages and layouts. - **Improvements** - Enhanced contract and asset import workflows with clearer UI, context-aware dialogs, and improved contract table variant handling. - Updated number input fields to remove browser-native spinners for a cleaner appearance. - Improved analytics event tracking for user interactions and deployment steps. - Refined UI components for consistent styling and accessibility. - Fixed sidebar badge placement and updated UI text for clarity. - Updated dialogs and payment embeds to reference transaction chain instead of active wallet chain and set default payment amounts. - Changed token supply label to "Circulating Supply" for clarity. - Replaced Chakra UI spinner and styled elements with local components for simplified UI. - **Bug Fixes** - Corrected UI typos and improved analytics event tracking for deployment errors. - **Documentation** - Added Storybook stories for new components to facilitate UI testing and documentation. - **Chores** - Refactored and extended types and props to support new asset and contract features, ensuring future extensibility. - Added tracking utilities for token deployment and asset creation analytics. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
24a556b
to
73629e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (3)
83-85
: Add form validation before proceeding to next stepCurrently, the code transitions to the next step without validating that the current form is valid. This could lead to incomplete or invalid data being used later in the flow.
onNext={() => { + // Validate form before proceeding + tokenInfoForm.trigger().then((isValid) => { + if (isValid) { setStep("distribution"); + } + }); }}
97-99
: Add form validation before proceeding to launch stepSimilar to the previous step transition, there's no validation before moving to the launch step.
onNext={() => { + // Validate form before proceeding + tokenDistributionForm.trigger().then((isValid) => { + if (isValid) { setStep("launch"); + } + }); }}
111-114
: Validate combined form data before passing to LaunchTokenStatusWhen combining form data for the launch step, no validation is performed to ensure the complete dataset is valid.
{step === "launch" && ( <LaunchTokenStatus client={props.client} onLaunchSuccess={props.onLaunchSuccess} onPrevious={() => { setStep("distribution"); }} createTokenFunctions={props.createTokenFunctions} + // Ensure both forms are valid before proceeding + isValid={tokenInfoForm.formState.isValid && tokenDistributionForm.formState.isValid} + form={{ + tokenInfoForm, + tokenDistributionForm, + }} values={{ ...tokenInfoForm.getValues(), ...tokenDistributionForm.getValues(), }} /> )}Then in LaunchTokenStatus component, add validation before beginning the launch sequence.
🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (2)
3-6
: Remove unused empty importsThere are several empty imports that don't bring any symbols into scope:
-import {} from "@/components/blocks/multi-step-status/multi-step-status"; -import {} from "@/components/ui/dialog"; -import {} from "lucide-react"; +// Remove unused importsThese should either be removed entirely or replaced with actual imports if the components are needed elsewhere in the file.
40-60
: Consider adding field labels and more descriptive initial valuesThe initial form values are mostly empty strings, which doesn't provide guidance to users about expected formats. Consider adding either:
- More descriptive placeholder values that show format examples
- Default values that make sense for common use cases
- Tooltip descriptions for each field
This would improve the user experience by making it clearer what information should be entered in each field.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (36)
apps/dashboard/src/@/actions/revalidate.ts
(1 hunks)apps/dashboard/src/@/components/blocks/distribution-chart.tsx
(1 hunks)apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
(1 hunks)apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
(1 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
(1 hunks)apps/dashboard/src/components/buttons/MismatchButton.tsx
(2 hunks)apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
(1 hunks)apps/dashboard/src/components/contract-components/import-contract/modal.tsx
(7 hunks)apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
(6 hunks)apps/dashboard/src/components/contract-components/tables/contract-table.tsx
(6 hunks)apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
(3 hunks)apps/dashboard/src/global.css
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (35)
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
- apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
- apps/dashboard/src/global.css
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
- apps/dashboard/src/@/actions/revalidate.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
- apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
- apps/dashboard/src/components/buttons/MismatchButton.tsx
- apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
- apps/dashboard/src/@/components/blocks/distribution-chart.tsx
- apps/dashboard/src/components/contract-components/tables/contract-table.tsx
- apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
- apps/dashboard/src/components/contract-components/import-contract/modal.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
- apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
- apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Unit Tests
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
const tokenDistributionForm = useForm<TokenDistributionFormValues>({ | ||
resolver: zodResolver(tokenDistributionFormSchema), | ||
values: { | ||
// sale fieldset | ||
saleAllocationPercentage: "0", | ||
salePrice: "0.1", | ||
supply: "1000000", | ||
saleEnabled: false, | ||
// airdrop | ||
airdropEnabled: false, | ||
airdropAddresses: [], | ||
}, | ||
reValidateMode: "onChange", | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation for numerical values
The form includes numerical values like saleAllocationPercentage
, salePrice
, and supply
that are currently stored as strings. While Zod validation likely handles this, consider adding:
- Proper type conversion in the form
- Min/max constraints for percentage values (0-100)
- Minimum values for price and supply to prevent invalid or negative values
This would provide better guidance to users and prevent errors during token creation.
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
between lines 62 and 75, the form fields for numerical values like
saleAllocationPercentage, salePrice, and supply are currently initialized as
strings without explicit input validation or type conversion. Update the form to
convert these fields to appropriate numeric types on input, enforce min/max
constraints such as 0 to 100 for saleAllocationPercentage, and set minimum
allowed values for salePrice and supply to prevent negative or invalid inputs.
This involves adjusting the form initialization values, adding validation rules
or schema refinements, and ensuring the UI input components reflect these
constraints for better user guidance and data integrity.
return ( | ||
<div> | ||
{step === "token-info" && ( | ||
<TokenInfoFieldset | ||
client={props.client} | ||
form={tokenInfoForm} | ||
onNext={() => { | ||
setStep("distribution"); | ||
}} | ||
/> | ||
)} | ||
|
||
{step === "distribution" && ( | ||
<TokenDistributionFieldset | ||
form={tokenDistributionForm} | ||
accountAddress={props.accountAddress} | ||
chainId={tokenInfoForm.watch("chain")} | ||
onPrevious={() => { | ||
setStep("token-info"); | ||
}} | ||
onNext={() => { | ||
setStep("launch"); | ||
}} | ||
/> | ||
)} | ||
|
||
{step === "launch" && ( | ||
<LaunchTokenStatus | ||
client={props.client} | ||
onLaunchSuccess={props.onLaunchSuccess} | ||
onPrevious={() => { | ||
setStep("distribution"); | ||
}} | ||
createTokenFunctions={props.createTokenFunctions} | ||
values={{ | ||
...tokenInfoForm.getValues(), | ||
...tokenDistributionForm.getValues(), | ||
}} | ||
/> | ||
)} | ||
</div> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add loading states during form transitions
The component doesn't manage loading states during step transitions or form submissions. Consider adding:
export function CreateTokenAssetPageUI(props: {
accountAddress: string;
client: ThirdwebClient;
createTokenFunctions: CreateTokenFunctions;
onLaunchSuccess: () => void;
}) {
const [step, setStep] = useState<"token-info" | "distribution" | "launch">(
"token-info",
);
+ const [isLoading, setIsLoading] = useState(false);
// ... rest of code
return (
<div>
{step === "token-info" && (
<TokenInfoFieldset
client={props.client}
form={tokenInfoForm}
+ isLoading={isLoading}
onNext={() => {
+ setIsLoading(true);
// Validate form before proceeding
tokenInfoForm.trigger().then((isValid) => {
if (isValid) {
setStep("distribution");
}
+ setIsLoading(false);
});
}}
/>
)}
// ... apply similar changes to other steps
This would provide visual feedback to users during transitions and prevent multiple submissions.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
between lines 77 and 118, the component lacks loading state management during
step transitions and form submissions. Add a loading state variable to track
when a transition or submission is in progress, update this state appropriately
in the onNext and onPrevious handlers, and conditionally render a loading
indicator or disable buttons to provide visual feedback and prevent multiple
submissions.
PR-Codex overview
This PR focuses on enhancing the asset creation workflow in the application, particularly for deploying and managing ERC-20 tokens. It introduces new tracking functionalities, UI components, and state management improvements.
Detailed summary
deploymentType
andcontractType
properties in various components.revalidatePathAction
for cache revalidation.Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation
Chores